﻿package demo.isolib {
	import de.polygonal.ds.Array2;
	
	import demo.isolib.Directions;
	import com.sshc625.log.Logger;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;

	/**
	 * Bitmap-blitter. The source must be a bitmap or sprite with 8 rows representing 8 directions.
	 * Each row contains an animation cycle where column 0 is for idle state.
	 *
	 * @author Anggie Bratadinata
	 */
	public class SpriteSheet extends Sprite {
		public static const FRAME_DELAY:Number = 64;

		//bitmap frames
		protected var _bitmapArray:Array2;
		//sheet source
		protected var _source:DisplayObject;
		//frame size
		protected var _frameWidth:Number;
		protected var _frameHeight:Number;
		//the rendered bitmap
		protected var _frameBitmap:Bitmap;
		protected var _frameRect:Rectangle;

		//current facing direction
		protected var _direction:String;
		//current frame row
		protected var _currentRow:Array;
		protected var _currentColNum:Number;
		//render timer
		protected var _oldTime:Number = 0;
		protected var _delay:Number;

		protected var _southFirst:Boolean;
		protected var _isReady:Boolean;
		protected var _isIdle:Boolean;

		protected var _isDirty:Boolean;

		public function SpriteSheet() {
			Logger.log("new SpriteSheet");
		}

		/**
		 * Build the sprite sheet. The source must contain 8 rows of animation frames arranged in clockwise order.
		 * The first row can be the south face or the south west.
		 * Acceptable row arrangements : S,SW,W,NW,N,NE,E,SE or SW,W,NW,N,NE,E,SE,S
		 *
		 * @param	source			The source Display object.
		 * @param	frameWidth		animation frame width
		 * @param	frameHeight		animation frame height
		 * @param	delay			delay between frames. This defines the animation speed in milliseconds
		 * @param	southFirst		the south face is on the first row (true) or the last (false)
		 */
		public function build(source:DisplayObject, frameWidth:Number, frameHeight:Number, delay:Number = SpriteSheet.FRAME_DELAY, southFirst:Boolean = true):void {
			Logger.log("SpriteSheet.build("+arguments+")");
			_source = source;
			_frameWidth = frameWidth;
			_frameHeight = frameHeight;
			_delay = delay;
			_southFirst = southFirst;

			var sourceBd:BitmapData = new BitmapData(source.width, source.height, true, 0x00000000);
			sourceBd.draw(source, null, null, null, null, true);

			var numCols:Number = Math.floor(source.width / frameWidth);
			var numRows:Number = Math.floor(source.height / frameHeight);

			_frameRect = new Rectangle(0, 0, frameWidth, frameHeight);

			_bitmapArray = new Array2(numCols, numRows);

			for (var i:int = 0; i < numRows; i++) {
				for (var j:int = 0; j < numCols; j++) {
					var bd:BitmapData = new BitmapData(frameWidth, frameHeight, true, 0x000000);
					_frameRect.x = j * frameWidth;
					_frameRect.y = i * frameHeight;

					bd.copyPixels(sourceBd, _frameRect, new Point(0, 0));

					_bitmapArray.set(j, i, bd);

				}
			}

			_frameRect.x = 0;
			_frameRect.y = 0;

			//if the south faces is on the last row, move it to the first
			if (!_southFirst) {
				_bitmapArray.shiftDown();
			}

			_isReady = true;

			setDirection(Directions.S);
			idle();

			_oldTime = getTimer();
			addEventListener(Event.ENTER_FRAME, onEnterFrame);

		}

		/**
		 * Set the state to idle
		 */
		public function idle():void {

			_isIdle = true;
			_isDirty = true;
		}
		;

		/**
		 * Set the state to walk/animated
		 *
		 */
		public function action():void {
			_isIdle = false;
			_isDirty = true;
		}
		;

		protected function onEnterFrame(e:Event = null):void {

			var elapsed:Number = getTimer() - _oldTime;

			if (elapsed >= _delay && _isDirty) {
				render();
				_oldTime = getTimer();
			}

		}

		protected function render():void {
			if (_frameBitmap == null) {
				_frameBitmap = new Bitmap(new BitmapData(_frameWidth, _frameHeight, true, 0x00000000));
				addChild(_frameBitmap);
			}

			_frameBitmap.bitmapData.lock();

			if (_isIdle) {
				_currentColNum = 0;
				_frameBitmap.bitmapData.copyPixels(_currentRow[_currentColNum], _frameRect, new Point(0, 0), null, null, false);
				//trace(this + "render idle");
				_isDirty = false;
			} else {

				if (_currentColNum < _currentRow.length - 1) {
					try {
						_frameBitmap.bitmapData.copyPixels(_currentRow[_currentColNum], _frameRect, new Point(0, 0), null, null, false);
					} catch (error:Error) {
						//trace("ERROR RENDERING : " + _currentColNum);
						return;
					}
					_currentColNum++;
				} else {
					_currentColNum = 1;
				}

				_isDirty = true;
			}

			_frameBitmap.bitmapData.unlock();
		}

		/**
		 * Set the active direction and make _currentRow point to a specific row in _bitmapArray
		 * @param	direction
		 */
		public function setDirection(direction:String):void {
			Logger.log("SpriteSheet.setDirection("+arguments+")");
			_direction = direction;
			var rowNum:Number;
			switch (direction) {
				case Directions.S:
					rowNum = 0;
					break;
				case Directions.SW:
					rowNum = 1;
					break;
				case Directions.W:
					rowNum = 2;
					break;
				case Directions.NW:
					rowNum = 3;
					break;
				case Directions.N:
					rowNum = 4;
					break;
				case Directions.NE:
					rowNum = 5;
					break;
				case Directions.E:
					rowNum = 6;
					break;
				case Directions.SE:
					rowNum = 7;
					break;
				default:
					rowNum = 0;
			}
			//trace(this + "setDirection, row " + rowNum);
			_currentRow = _bitmapArray.getRow(rowNum);

			_isDirty = true;
			render();

		}

		/**
		 * Get currently rendered frame
		 *
		 * @return Bitmap
		 */
		public function getFrameBitmap():Bitmap {
			return _frameBitmap;
		}

		/**
		 * Get current direction
		 * @return
		 */
		public function getDirection():String {
			return _direction;
		}
		
	}
}

